//##############################################################################
// Text.js
//##############################################################################

function QuestionText() {
    "use strict";


// constructor:
    /**
     * Display one or more lines of dynamic text (not user editable) in the display list. Line wrapping support (using the
     * lineWidth) is very basic, wrapping on spaces and tabs only. Note that as an alternative to Text, you can position HTML
     * text above or below the canvas relative to items in the display list using the {{#crossLink "DisplayObject/localToGlobal"}}{{/crossLink}}
     * method, or using {{#crossLink "DOMElement"}}{{/crossLink}}.
     *
     * <b>Please note that Text does not support HTML text, and can only display one font style at a time.</b> To use
     * multiple font styles, you will need to create multiple text instances, and position them manually.
     *
     * <h4>Example</h4>
     *
     *      var text = new createjs.Text("Hello World", "20px Arial", "#ff7700");
     *      text.x = 100;
     *      text.textBaseline = "alphabetic";
     *
     * CreateJS Text supports web fonts (the same rules as Canvas). The font must be loaded and supported by the browser
     * before it can be displayed.
     *
     * <strong>Note:</strong> Text can be expensive to generate, so cache instances where possible. Be aware that not all
     * browsers will render Text exactly the same.
     * @class Text
     * @extends DisplayObject
     * @constructor
     * @param {String} [text] The text to display.
     * @param {String} [font] The font style to use. Any valid value for the CSS font attribute is acceptable (ex. "bold
     * 36px Arial").
     * @param {String} [color] The color to draw the text in. Any valid value for the CSS color attribute is acceptable (ex.
     * "#F00", "red", or "#FF0000").
     **/
    function Text(text, font, color) {
        this.DisplayObject_constructor();

        this.lineCount = null;


        // public properties:
        /**
         * The text to display.
         * @property text
         * @type String
         **/
        this.text = text;

        /**
         * The font style to use. Any valid value for the CSS font attribute is acceptable (ex. "bold 36px Arial").
         * @property font
         * @type String
         **/
        this.font = font;

        /**
         * The color to draw the text in. Any valid value for the CSS color attribute is acceptable (ex. "#F00"). Default is "#000".
         * It will also accept valid canvas fillStyle values.
         * @property color
         * @type String
         **/
        this.color = color;

        /**
         * The horizontal text alignment. Any of "start", "end", "left", "right", and "center". For detailed
         * information view the
         * <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#text-styles">
         * whatwg spec</a>. Default is "left".
         * @property textAlign
         * @type String
         **/
        this.textAlign = "left";

        /**
         * The vertical alignment point on the font. Any of "top", "hanging", "middle", "alphabetic", "ideographic", or
         * "bottom". For detailed information view the <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#text-styles">
         * whatwg spec</a>. Default is "top".
         * @property textBaseline
         * @type String
         */
        this.textBaseline = "top";

        /**
         * The maximum width to draw the text. If maxWidth is specified (not null), the text will be condensed or
         * shrunk to make it fit in this width. For detailed information view the
         * <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#text-styles">
         * whatwg spec</a>.
         * @property maxWidth
         * @type Number
         */
        this.maxWidth = null;

        /**
         * If greater than 0, the text will be drawn as a stroke (outline) of the specified width.
         * @property outline
         * @type Number
         **/
        this.outline = 0;

        /**
         * Indicates the line height (vertical distance between baselines) for multi-line text. If null or 0,
         * the value of getMeasuredLineHeight is used.
         * @property lineHeight
         * @type Number
         **/
        this.lineHeight = 0;

        /**
         * Indicates the maximum width for a line of text before it is wrapped to multiple lines. If null,
         * the text will not be wrapped.
         * @property lineWidth
         * @type Number
         **/
        this.lineWidth = null;
    }
    var p = createjs.extend(Text, createjs.DisplayObject);

    // TODO: deprecated
    // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.


// static properties:
    /**
     * @property _workingContext
     * @type CanvasRenderingContext2D
     * @private
     **/
    var canvas = (createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"));
    if (canvas.getContext) { Text._workingContext = canvas.getContext("2d"); canvas.width = canvas.height = 1; }


// constants:
    /**
     * Lookup table for the ratio to offset bounds x calculations based on the textAlign property.
     * @property H_OFFSETS
     * @type Object
     * @protected
     * @static
     **/
    Text.H_OFFSETS = {start: 0, left: 0, center: -0.5, end: -1, right: -1};

    /**
     * Lookup table for the ratio to offset bounds y calculations based on the textBaseline property.
     * @property H_OFFSETS
     * @type Object
     * @protected
     * @static
     **/
    Text.V_OFFSETS = {top: 0, hanging: -0.01, middle: -0.4, alphabetic: -0.8, ideographic: -0.85, bottom: -1};


// public methods:
    /**
     * Returns true or false indicating whether the display object would be visible if drawn to a canvas.
     * This does not account for whether it would be visible within the boundaries of the stage.
     * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
     * @method isVisible
     * @return {Boolean} Whether the display object would be visible if drawn to a canvas
     **/
    p.isVisible = function() {
        var hasContent = this.cacheCanvas || (this.text != null && this.text !== "");
        return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
    };

    /**
     * Draws the Text into the specified context ignoring its visible, alpha, shadow, and transform.
     * Returns true if the draw was handled (useful for overriding functionality).
     * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
     * @method draw
     * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
     * @param {Boolean} ignoreCache Indicates whether the draw operation should ignore any current cache.
     * For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
     * into itself).
     **/
    p.draw = function(ctx, ignoreCache) {
        if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; }

        var col = this.color || "#000";
        if (this.outline) { ctx.strokeStyle = col; ctx.lineWidth = this.outline*1; }
        else { ctx.fillStyle = col; }

        this._drawText(this._prepContext(ctx));
        return true;
    };

    /**
     * Returns the measured, untransformed width of the text without wrapping. Use getBounds for a more robust value.
     * @method getMeasuredWidth
     * @return {Number} The measured, untransformed width of the text.
     **/
    p.getMeasuredWidth = function() {
        return this._getMeasuredWidth(this.text);
    };

    /**
     * Returns an approximate line height of the text, ignoring the lineHeight property. This is based on the measured
     * width of a "M" character multiplied by 1.2, which provides an approximate line height for most fonts.
     * @method getMeasuredLineHeight
     * @return {Number} an approximate line height of the text, ignoring the lineHeight property. This is
     * based on the measured width of a "M" character multiplied by 1.2, which approximates em for most fonts.
     **/
    p.getMeasuredLineHeight = function() {
        return this._getMeasuredWidth("M")*1.2;
    };

    /**
     * Returns the approximate height of multi-line text by multiplying the number of lines against either the
     * <code>lineHeight</code> (if specified) or {{#crossLink "Text/getMeasuredLineHeight"}}{{/crossLink}}. Note that
     * this operation requires the text flowing logic to run, which has an associated CPU cost.
     * @method getMeasuredHeight
     * @return {Number} The approximate height of the untransformed multi-line text.
     **/
    p.getMeasuredHeight = function() {
        return this._drawText(null,{}).height;
    };

    /**
     * Docced in superclass.
     */
    p.getBounds = function() {
        var rect = this.DisplayObject_getBounds();
        if (rect) { return rect; }
        if (this.text == null || this.text === "") { return null; }
        var o = this._drawText(null, {});
        var w = (this.maxWidth && this.maxWidth < o.width) ? this.maxWidth : o.width;
        var x = w * Text.H_OFFSETS[this.textAlign||"left"];
        var lineHeight = this.lineHeight||this.getMeasuredLineHeight();
        var y = lineHeight * Text.V_OFFSETS[this.textBaseline||"top"];
        return this._rectangle.setValues(x, y, w, o.height);
    };

    /**
     * Returns an object with width, height, and lines properties. The width and height are the visual width and height
     * of the drawn text. The lines property contains an array of strings, one for
     * each line of text that will be drawn, accounting for line breaks and wrapping. These strings have trailing
     * whitespace removed.
     * @method getMetrics
     * @return {Object} An object with width, height, and lines properties.
     **/
    p.getMetrics = function() {
        var o = {lines:[]};
        o.lineHeight = this.lineHeight || this.getMeasuredLineHeight();
        o.vOffset = o.lineHeight * Text.V_OFFSETS[this.textBaseline||"top"];
        return this._drawText(null, o, o.lines);
    };

    /**
     * Returns a clone of the Text instance.
     * @method clone
     * @return {Text} a clone of the Text instance.
     **/
    p.clone = function() {
        return this._cloneProps(new Text(this.text, this.font, this.color));
    };

    /**
     * Returns a string representation of this object.
     * @method toString
     * @return {String} a string representation of the instance.
     **/
    p.toString = function() {
        return "[Text (text="+  (this.text.length > 20 ? this.text.substr(0, 17)+"..." : this.text) +")]";
    };


// private methods:
    /**
     * @method _cloneProps
     * @param {Text} o
     * @protected
     * @return {Text} o
     **/
    p._cloneProps = function(o) {
        this.DisplayObject__cloneProps(o);
        o.textAlign = this.textAlign;
        o.textBaseline = this.textBaseline;
        o.maxWidth = this.maxWidth;
        o.outline = this.outline;
        o.lineHeight = this.lineHeight;
        o.lineWidth = this.lineWidth;
        return o;
    };

    /**
     * @method _getWorkingContext
     * @param {CanvasRenderingContext2D} ctx
     * @return {CanvasRenderingContext2D}
     * @protected
     **/
    p._prepContext = function(ctx) {
        ctx.font = this.font||"10px sans-serif";
        ctx.textAlign = this.textAlign||"left";
        ctx.textBaseline = this.textBaseline||"top";
        ctx.lineJoin = "miter";
        ctx.miterLimit = 2.5;
        return ctx;
    };

    /**
     * Draws multiline text.
     * @method _drawText
     * @param {CanvasRenderingContext2D} ctx
     * @param {Object} o
     * @param {Array} lines
     * @return {Object}
     * @protected
     **/
    p._drawText = function(ctx, o, lines) {
        var paint = !!ctx;
        if (!paint) {
            ctx = Text._workingContext;
            ctx.save();
            this._prepContext(ctx);
        }
        var lineHeight = this.lineHeight||this.getMeasuredLineHeight();

        var maxW = 0, count = 0;
        var hardLines = String(this.text).split(/(?:\r\n|\r|\n)/);

        for (var i=0, l=hardLines.length; i<l; i++) {
            var str = hardLines[i];
            var w = null;

            if (this.lineWidth != null && (w = ctx.measureText(str).width) > this.lineWidth) {
                // text wrapping:
                var words = str.split(/(\s|\n|[\u4e00-\u9fa5]+)/);//按照中文和空格来分割

                var splitChineseWords = [];
                for(var wordIndex = 0; wordIndex < words.length; wordIndex++)
                {
                    var chineseWordStr = words[wordIndex];
                    if(chineseWordStr == "")
                        continue;
                    if((/([\u4e00-\u9fa5]+)/).test(chineseWordStr))
                    {
                        splitChineseWords = splitChineseWords.concat(chineseWordStr.split(""));//再把中文拆分成一个一个的
                    }
                    else
                    {
                        if((/([\u2610]+)/).test(chineseWordStr)){
                            splitChineseWords = splitChineseWords.concat(chineseWordStr.split(""));//再把中文拆分成一个一个的
                        } else {
                            // console.log((/(\n+)/).test(chineseWordStr))
                            if(chineseWordStr!="\\n"&&chineseWordStr.indexOf("\\n")>=0) {
                                let ws = chineseWordStr.split("\\n");

                                for (let j = 0; j < ws.length; j++) {
                                    if(ws[j].length>0) {
                                        splitChineseWords.push(ws[j]);

                                        if(j<ws.length-1) {
                                            splitChineseWords.push("\\n");
                                        }
                                    }
                                }
                            } else {
                                splitChineseWords.push(chineseWordStr);
                            }
                        }
                    }
                }

                words = splitChineseWords;//重新组成数组
                str = words[0];
                w = ctx.measureText(str).width;

                if((/([\u2610]+)/).test(str)){
                    var text_width = ctx.measureText(str).width;
                    var t_w = (lineHeight-text_width-10)<0?text_width:(lineHeight-10);

                    w += t_w-text_width;
                }

                for (var j=1, jl=words.length; j<jl; j+=2) {
                    // Line needs to wrap:
                    var nextStr = j+1 < jl ? words[j+1] : "";
                    var wordW = ctx.measureText(words[j] + nextStr).width;

                    if((/([\u2610]+)/).test(words[j])){
                        var text_width = ctx.measureText(words[j]).width;
                        var t_w = (lineHeight-text_width-10)<0?text_width:(lineHeight-10);
                        wordW += t_w-text_width;
                    }

                    if((/([\u2610]+)/).test(nextStr)){
                        var text_width = ctx.measureText(nextStr).width;
                        var t_w = (lineHeight-text_width-10)<0?text_width:(lineHeight-10);
                        wordW += t_w-text_width+6;
                    }

                    if((/([\u2610]+)/).test(words[j])&&!(/([\u2610]+)/).test(nextStr)){
                        wordW+=6
                    }

                    if(words[j].indexOf("\\n")>=0||nextStr=="\\n"){
                        wordW = this.lineWidth-w+10;
                    }

                    if (w + wordW > this.lineWidth) {
                        if(words[j] != "\s") { //原版没有这个IF
                            var text_width = ctx.measureText(words[j]).width;
                            var t_w = text_width;

                            if((/([\u2610]+)/).test(words[j])) {
                                t_w = (lineHeight-text_width-10)<0?text_width:(lineHeight-10);
                            }

                            if(w+t_w>this.lineWidth) {
                                if (paint) {
                                    if(this.lineCount!=null&&(count+1)>=this.lineCount) {
                                        str = str.substring(0, str.length-2);
                                        str += "...";
                                    }

                                    this._drawTextLine(ctx, str, count*lineHeight);
                                }
                                if (lines) { lines.push(str); }
                                if (w > maxW) { maxW = w; }
                                str = words[j];

                                w = ctx.measureText(str).width;
                                count++;
                                j--;
                                if(this.lineCount!=null&&count>=this.lineCount) {
                                    break;
                                } else {
                                    continue;
                                }
                            } else {
                                if(words[j] != "\\n") {
                                     //英文时为空格，不需要加，中文时为汉字，所以不能漏了
                                    if(this.lineCount!=null&&(count+1)>=this.lineCount) {
                                        str = str.substring(0, str.length-2);
                                        str += "...";

                                        this._drawTextLine(ctx, str, count*lineHeight);

                                        if (lines) { lines.push(str); }
                                        if (w > maxW) { maxW = w; }
                                        str = words[j];

                                        w = ctx.measureText(str).width;
                                        count++;
                                        j--;

                                        break;
                                    } else {
                                        str += words[j];
                                    }
                                }
                            }
                        }

                        if (paint) {
                            str = str.replace(/\\n/g, "");
                            this._drawTextLine(ctx, str, count*lineHeight);
                        }

                        if (lines) { lines.push(str); }
                        if (w > maxW) { maxW = w; }
                        str = nextStr;
                        w = ctx.measureText(str).width;
                        count++;
                    } else {
                        str += words[j] + nextStr;
                        w += wordW;
                    }
                }
            }

            if (paint) {
                if(this.lineCount!=null) {
                    if(count<this.lineCount) {
                        if (this.lineWidth != null && (w = ctx.measureText(str).width) > this.lineWidth) {
                            var words = str.split("");
                            var str = "";
                            var w = 0;
                            for(var j=0, jl=words.length; j<jl; j+=2) {
                                var nextStr = j+1 < jl ? words[j+1] : "";
                                var wordW = ctx.measureText(words[j] + nextStr).width;

                                if (w + wordW > this.lineWidth) {
                                    str = str.substring(0, str.length-2);
                                    str += "...";
                                    this._drawTextLine(ctx, str, count*lineHeight);
                                    break;
                                } else {
                                    str += words[j] + nextStr;
                                    w += wordW;
                                }
                            }
                        } else {
                            this._drawTextLine(ctx, str, count*lineHeight);
                        }
                    }
                } else {
                    str = str.replace(/\\n/g, "");
                    this._drawTextLine(ctx, str, count*lineHeight);
                }
            }
            if (lines) { lines.push(str); }
            if (o && w == null) { w = ctx.measureText(str).width; }
            if (w > maxW) { maxW = w; }
            count++;
        }

        if (o) {
            o.width = maxW;
            o.height = count*lineHeight;
        }
        if (!paint) { ctx.restore(); }
        return o;
    };


    /**
     * @method _drawTextLine
     * @param {CanvasRenderingContext2D} ctx
     * @param {String} text
     * @param {Number} y
     * @protected
     **/
    p._drawTextLine = function(ctx, text, y) {
        // Chrome 17 will fail to draw the text if the last param is included but null, so we feed it a large value instead:
        if (this.outline) { ctx.strokeText(text, 0, y, this.maxWidth||0xFFFF); }
        else {
            let words = text.split(/(\s|[\u2610]+)/);
            var lineHeight = this.lineHeight||this.getMeasuredLineHeight();
            let x = 0;
            for(let i=0;i<words.length;i++) {

                if((/([\u2610]+)/).test(words[i])){
                    let lineWidth = ctx.lineWidth;
                    let strokeStyle = ctx.strokeStyle;

                    ctx.strokeStyle = ctx.fillStyle;
                    ctx.lineWidth = 3;

                    let text_width = ctx.measureText(words[i]).width/words[i].length;
                    let w = (lineHeight-text_width-10)<0?text_width:(lineHeight-10);

                    x+=6
                    for(let j=0;j<words[i].length;j++) {
                        ctx.strokeRect(x+j*w, y+(text_width-w)/2, w, w);
                        x+=6
                    }

                    x += (words[i].length*w);
                    ctx.strokeStyle = strokeStyle;
                    ctx.lineWidth = lineWidth;
                } else {
                    ctx.fillText(words[i], x, y, this.maxWidth||0xFFFF);
                    x += ctx.measureText(words[i]).width;
                }
            }
        }
    };


    /**
     * @method _getMeasuredWidth
     * @param {String} text
     * @protected
     **/
    p._getMeasuredWidth = function(text) {
        var ctx = Text._workingContext;
        ctx.save();
        var w = this._prepContext(ctx).measureText(text).width;
        ctx.restore();
        return w;
    };

    p.Text = createjs.promote(Text, "DisplayObject");
    return p;
};